home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / kernel / help.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-03  |  34.5 KB  |  1,276 lines  |  [TEXT/KAHL]

  1. /* Help support for Xconq.
  2.    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995
  3.    Stanley T. Shebs.
  4.  
  5. Xconq is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.  See the file COPYING.  */
  9.  
  10. /* This is basically support code for interfaces, which handle the
  11.    actual help interaction themselves. */
  12.  
  13. /* This file must also be translated (mostly) for non-English Xconq. */
  14.  
  15. #include "conq.h"
  16.  
  17. /* The first help node in the chain, usually the list of topics. */
  18.  
  19. HelpNode *firsthelpnode = NULL;
  20.  
  21. /* The last help node. */
  22.  
  23. HelpNode *lasthelpnode = NULL;
  24.  
  25. /* Create an empty help node. */
  26.  
  27. HelpNode *
  28. create_help_node()
  29. {
  30.     HelpNode *node = (HelpNode *) xmalloc(sizeof(HelpNode));
  31.  
  32.     node->key = NULL;
  33.     node->fn = NULL;
  34.     node->nclass = miscnode;
  35.     node->arg = 0;
  36.     node->text = NULL;
  37.     node->prev = node->next = NULL;
  38.     return node;
  39. }
  40.  
  41. /* Add a help node after the given node. */
  42.  
  43. HelpNode *
  44. add_help_node(key, fn, arg, prevnode)
  45. char *key;
  46. void (*fn)();
  47. int arg;
  48. HelpNode *prevnode;
  49. {
  50.     HelpNode *node, *nextnode;
  51.  
  52.     node = create_help_node();
  53.     node->key = key;
  54.     node->fn = fn;
  55.     node->arg = arg;
  56.     if (prevnode != NULL) {
  57.     nextnode = prevnode->next;
  58.     } else {
  59.     prevnode = lasthelpnode;
  60.     nextnode = firsthelpnode;
  61.     }
  62.     node->prev = prevnode;
  63.     node->next = nextnode;
  64.     prevnode->next = node;
  65.     nextnode->prev = node;
  66.     /* Might need to fix last help node. */
  67.     lasthelpnode = firsthelpnode->prev;
  68.     return node;
  69. }
  70.  
  71. /* Given a string and node, find the next node whose key matches. */
  72.  
  73. HelpNode *
  74. find_help_node(node, str)
  75. HelpNode *node;
  76. char *str;
  77. {
  78.     HelpNode *tmp;
  79.  
  80.     /* Note that the search wraps around. */
  81.     for (tmp = node->next; tmp != node; tmp = tmp->next) {
  82.         if (strcmp(tmp->key, str) == 0)
  83.           return tmp;
  84.         if (strstr(tmp->key, str) != NULL)
  85.           return tmp;
  86.     }
  87.     return NULL;
  88. }
  89.  
  90. /* Create the initial help node, which is the copyright info, and link
  91.    it to itself.  Subsequent nodes will be inserted later, after a game
  92.    has been loaded. */
  93.  
  94. void
  95. init_help()
  96. {
  97.     firsthelpnode = create_help_node();
  98.     firsthelpnode->key = "copyright";
  99.     firsthelpnode->fn = describe_copyright;
  100.     firsthelpnode->prev = firsthelpnode->next = firsthelpnode;
  101.     lasthelpnode = firsthelpnode;
  102.     add_help_node("news", describe_news, 0, NULL);
  103. }
  104.  
  105. /* This function creates the actual set of help nodes for the kernel. */
  106.  
  107. void
  108. create_game_help_nodes()
  109. {
  110.     int u, m, t;
  111.     char *name, *longname;
  112.     HelpNode *node;
  113.  
  114.     add_help_node("game design", describe_game_design, 0, NULL);
  115.     add_help_node("modules", describe_game_modules, 0, NULL);
  116.     add_help_node("scoring", describe_scorekeepers, 0, NULL);
  117.     for_all_unit_types(u) {
  118.     longname = u_long_name(u);
  119.     if (!empty_string(longname)) {
  120.         sprintf(spbuf, "%s (%s)", longname, u_type_name(u));
  121.         name = copy_string(spbuf);
  122.     } else {
  123.         name = u_type_name(u);
  124.     }
  125.     node = add_help_node(name, describe_utype, u, NULL);
  126.     node->nclass = utypenode;
  127.     }
  128.     for_all_material_types(m) {
  129.     node = add_help_node(m_type_name(m), describe_mtype, m, NULL);
  130.     node->nclass = mtypenode;
  131.     }
  132.     for_all_terrain_types(t) {
  133.     node = add_help_node(t_type_name(t), describe_ttype, t, NULL);
  134.     node->nclass = ttypenode;
  135.     }
  136.     add_help_node("general concepts", describe_concepts, 0, NULL);
  137.     /* Invalidate any existing topics node. */
  138.     firsthelpnode->text = NULL;
  139. }
  140.  
  141. /* Return the string containing the text of the help node, possibly
  142.    computing it first. */
  143.  
  144. char *
  145. get_help_text(node)
  146. HelpNode *node;
  147. {
  148.     if (node != NULL) {
  149.     /* Maybe calculate the text to display. */
  150.     if (node->text == NULL) {
  151.         if (node->fn != NULL) {
  152.             node->text = xmalloc(5000);
  153.         if (node->text != NULL) {
  154.             /* Make buffer into an empty string. */
  155.             (node->text)[0] = '\0';
  156.             node->textend = 0;
  157.             node->textsize = 5000;
  158.             (*(node->fn))(node->arg, node->key, node->text);
  159.             node->textend = strlen(node->text);
  160.         } else {
  161.             /* Ran out of memory... */
  162.         }
  163.         } else {
  164.         /* Generate a default message if nothing to compute help. */
  165.         sprintf(spbuf, "%s: No info available.", node->key);
  166.         node->text = copy_string(spbuf);
  167.         node->textend = strlen(node->text);
  168.         }
  169.     }
  170.     return node->text;
  171.     } else {
  172.     return NULL;
  173.     }
  174. }
  175.  
  176. void
  177. describe_copyright(arg, key, buf)
  178. int arg;
  179. char *key, *buf;
  180. {
  181.     tprintf(buf, "Xconq is free software.");
  182. }
  183.  
  184. /* Create a raw list of help topics by just iterating through all the nodes,
  185.    except for the topics node itself. */
  186.  
  187. void
  188. describe_topics(arg, key, buf)
  189. int arg;
  190. char *key, *buf;
  191. {
  192.     HelpNode *topics, *tmp;
  193.  
  194.     topics = find_help_node(firsthelpnode, "topics");
  195.     /* Unlikely that we'll call this without the topics node existing
  196.        already, but just in case... */
  197.     if (topics == NULL)
  198.       return;
  199.     for (tmp = topics->next; tmp != topics; tmp = tmp->next) {
  200.     tprintf(buf, "%s", tmp->key);
  201.     tprintf(buf, "\n");
  202.     }
  203. }
  204.  
  205. /* Get the news file and put it into text buffer. */
  206.  
  207. void
  208. describe_news(arg, key, buf)
  209. int arg;
  210. char *key, *buf;
  211. {
  212.     FILE *fp;
  213.  
  214.     fp = fopen(news_filename(), "r");
  215.     if (fp != NULL) {
  216.     tprintf(buf, "XCONQ NEWS\n\n");
  217.     while (fgets(spbuf, BUFSIZE-1, fp) != NULL) {
  218.         tprintf(buf, "%s", spbuf);
  219.     }
  220.     fclose(fp);
  221.     } else {
  222.     tprintf(buf, "(no news)");
  223.     }
  224. }
  225.  
  226. void
  227. describe_concepts(arg, key, buf)
  228. int arg;
  229. char *key, *buf;
  230. {
  231.     tprintf(buf, "Action points (Acp) are what units need to do ");
  232.     tprintf(buf, "anything at all.");
  233.     tprintf(buf, "\n");
  234. }
  235.  
  236. /* Spit out all the general game_design parameters in a readable fashion. */
  237.  
  238. void
  239. describe_game_design(arg, key, buf)
  240. int arg;
  241. char *key, *buf;
  242. {
  243.     int u, m, t;
  244.     
  245.     /* Replicate title and blurb? (should put title at head of windows, and pages if printed) */
  246.     tprintf(buf, "This game includes %d unit types and %d terrain types",
  247.         numutypes, numttypes);
  248.     if (nummtypes > 0) {
  249.     tprintf(buf, ", along with %d material types", nummtypes);
  250.     }
  251.     tprintf(buf, ".\n");
  252.     if (g_sides_min() == g_sides_max()) {
  253.         tprintf(buf, "Exactly %d sides may play.\n", g_sides_min());
  254.     } else {
  255.         tprintf(buf, "From %d up to %d sides may play.\n", g_sides_min(), g_sides_max());
  256.     }
  257.     tprintf(buf, "\n");
  258.     tprintf(buf, "Player advantages may range from %d to %d, defaulting to %d.\n",
  259.         g_advantage_min(), g_advantage_max(), g_advantage_default());
  260.     tprintf(buf, "\n");
  261.     if (1 /* will/did do country placing */) {
  262.         tprintf(buf, "If randomly generated, countries are %d cells across, between %d and %d cells apart.\n",
  263.         2 * g_min_radius() + 1,
  264.         g_min_separation(), g_max_separation());
  265.     }
  266.     tprintf(buf, "\n");
  267.     if (g_see_all()) {
  268.     tprintf(buf, "Everything is always seen by all sides.\n");
  269.     } else {
  270.         if (g_see_terrain_always()) {
  271.         tprintf(buf, "Terrain view is always accurate once seen.\n");
  272.         }
  273.         if (g_see_weather_always()) {
  274.         tprintf(buf, "Weather view is always accurate once terrain seen.\n");
  275.         }
  276.         if (g_terrain_seen()) {
  277.         tprintf(buf, "World terrain is already seen by all sides.\n");
  278.         }
  279.     }
  280.     tprintf(buf, "\n");
  281.     if (g_last_turn() < 9999) {
  282.     tprintf(buf, "Game can go for up to %d turns", g_last_turn());
  283.     if (g_extra_turn() > 0) {
  284.         tprintf(buf, ", with %d%% chance of additional turn thereafter.", g_extra_turn());
  285.     }
  286.     tprintf(buf, ".\n");
  287.     }
  288.     if (g_rt_for_game() > 0) {
  289.     tprintf(buf, "Entire game can last up to %d minutes.\n",
  290.         g_rt_for_game() / 60);
  291.     }
  292.     if (g_rt_per_turn() > 0) {
  293.     tprintf(buf, "Each turn can last up to %d minutes.\n",
  294.         g_rt_per_turn() / 60);
  295.     }
  296.     if (g_rt_per_side() > 0) {
  297.     tprintf(buf, "Each side gets a total %d minutes to act.\n",
  298.         g_rt_per_side() / 60);
  299.     }
  300.     if (g_units_in_game_max() >= 0) {
  301.     tprintf(buf, "Limited to no more than %d units in all.\n", g_units_in_game_max());
  302.     }
  303.     if (g_units_per_side() >= 0) {
  304.     tprintf(buf, "Limited to no more than %d units per side.\n", g_units_per_side());
  305.     }
  306.     /* (should list out random event types?) */
  307.     tprintf(buf, "\n");
  308.     tprintf(buf, "Lowest possible temperature is %d, at an elevation of %d.\n",
  309.         g_temp_floor(), g_temp_floor_elev());
  310.     tprintf(buf, "\nUnit Types:\n");
  311.     for_all_unit_types(u) {
  312.     tprintf(buf, "  %s", u_type_name(u));
  313.     if (!empty_string(u_help(u)))
  314.       tprintf(buf, " (%s)", u_help(u));
  315.     tprintf(buf, "\n");
  316.     /* Show designers a bit more. */
  317.     if (numdesigners > 0) {
  318.         tprintf(buf, "    [");
  319.         if (!empty_string(u_uchar(u)))
  320.           tprintf(buf, "char '%s'", u_uchar(u));
  321.         else
  322.           tprintf(buf, "no char");
  323.         if (!empty_string(u_image_name(u)))
  324.           tprintf(buf, ", image \"%s\"", u_image_name(u));
  325.         if (!empty_string(u_color(u)))
  326.           tprintf(buf, ", color \"%s\"", u_color(u));
  327.         if (!empty_string(u_generic_name(u)))
  328.           tprintf(buf, ", generic name \"%s\"", u_generic_name(u));
  329.         if (u_desc_format(u) != lispnil) {
  330.             tprintf(buf, ", special format");
  331.         }
  332.         tprintf(buf, "]\n");
  333.     }
  334.     }
  335.     tprintf(buf, "\nTerrain Types:\n");
  336.     for_all_terrain_types(t) {
  337.     tprintf(buf, "  %s", t_type_name(t));
  338.     if (!empty_string(t_help(t)))
  339.       tprintf(buf, " (%s)", t_help(t));
  340.     tprintf(buf, "\n");
  341.     /* Show designers a bit more. */
  342.     if (numdesigners > 0) {
  343.         tprintf(buf, "    [");
  344.         if (!empty_string(t_char(t)))
  345.           tprintf(buf, "char '%s'", t_char(t));
  346.         else
  347.           tprintf(buf, "no char");
  348.         if (!empty_string(t_image_name(t)))
  349.           tprintf(buf, ", image \"%s\"", t_image_name(t));
  350.         if (!empty_string(t_color(t)))
  351.           tprintf(buf, ", color \"%s\"", t_color(t));
  352.         if (t_desc_format(t) != lispnil) {
  353.             tprintf(buf, ", special format");
  354.         }
  355.         tprintf(buf, "]\n");
  356.     }
  357.     }
  358.     if (nummtypes > 0) {
  359.     tprintf(buf, "\nMaterial Types:\n");
  360.     for_all_material_types(m) {
  361.         tprintf(buf, "  %s", m_type_name(m));
  362.         if (!empty_string(m_help(m)))
  363.           tprintf(buf, " (%s)", m_help(m));
  364.         tprintf(buf, "\n");
  365.         /* Show designers a bit more. */
  366.         if (numdesigners > 0) {
  367.         tprintf(buf, "    [");
  368.         if (!empty_string(m_char(m)))
  369.           tprintf(buf, "char '%s'", m_char(m));
  370.         else
  371.           tprintf(buf, "no char");
  372.         if (!empty_string(m_image_name(m)))
  373.           tprintf(buf, ", image \"%s\"", m_image_name(m));
  374.         if (!empty_string(m_color(m)))
  375.           tprintf(buf, ", color \"%s\"", m_color(m));
  376.         if (m_desc_format(m) != lispnil) {
  377.             tprintf(buf, ", special format");
  378.         }
  379.         tprintf(buf, "]\n");
  380.         }
  381.     }
  382.     }
  383. }
  384.  
  385. int any_mp_to_enter_unit PROTO ((int u));
  386. int any_mp_to_leave_unit PROTO ((int u));
  387.  
  388. int
  389. any_mp_to_enter_unit(u)
  390. int u;
  391. {
  392.     int u2;
  393.     
  394.     for_all_unit_types(u2) {
  395.     if (uu_mp_to_enter(u, u2) != 0) return TRUE;
  396.     }
  397.     return FALSE;
  398. }
  399.  
  400. int
  401. any_mp_to_leave_unit(u)
  402. int u;
  403. {
  404.     int u2;
  405.     
  406.     for_all_unit_types(u2) {
  407.     if (uu_mp_to_leave(u, u2) != 0) return TRUE;
  408.     }
  409.     return FALSE;
  410. }
  411.  
  412. void fraction_desc PROTO ((char *buf, int n));
  413.  
  414. void
  415. fraction_desc(buf, n)
  416. char *buf;
  417. int n;
  418. {
  419.     sprintf(buf, "%d.%d", n / 100, n % 100);
  420. }
  421.  
  422. /* Full details on the given type of unit. */
  423.  
  424. /* (The defaults should come from the *.def defaults!!) */
  425.  
  426. void
  427. describe_utype(u, key, buf)
  428. int u;
  429. char *key, *buf;
  430. {
  431.     int m;
  432.  
  433.     append_help_phrase(buf, u_help(u));
  434.     if (u_point_value(u) > 0) {
  435.         tprintf(buf, "     (point value %d)\n", u_point_value(u));
  436.     }
  437.     if (u_can_be_self(u)) {
  438.         tprintf(buf, "Can be self-unit.\n");
  439.     }
  440.     if (u_possible_sides(u) != lispnil) {
  441.         tprintf(buf, "Some limitations on possible sides.\n");
  442.     }
  443.     if (u_type_in_game_max(u) >= 0) {
  444.         tprintf(buf, "At most %d allowed in a game.\n", u_type_in_game_max(u));
  445.     }
  446.     if (u_type_per_side_max(u) >= 0) {
  447.         tprintf(buf, "At most %d allowed on each side in a game.\n", u_type_per_side_max(u));
  448.     }
  449.     if (u_acp(u) > 0) {
  450.         tprintf(buf, "Gets %d action points each turn", u_acp(u));
  451.         if (u_acp_min(u) != 0) {
  452.         tprintf(buf, ", can go down to %d acp", u_acp_min(u));
  453.         }
  454.         if (u_acp_max(u) != -1) {
  455.         tprintf(buf, ", can go up to %d acp", u_acp_max(u));
  456.         }
  457.         if (u_free_acp(u) != 0) {
  458.         tprintf(buf, ", %d free", u_free_acp(u));
  459.         }
  460.         tprintf(buf, ".\n");
  461.     } else {
  462.     tprintf(buf, "Does not act.\n");
  463.     }
  464.     if (u_speed(u) > 0) {
  465.     tprintf(buf, "Speed (mp/acp ratio) is %d.%d cells/acp.\n",
  466.         u_speed(u) / 100, u_speed(u) % 100);
  467.     if (u_speed_min(u) != 0 || u_speed_max(u) != 9999) {
  468.         tprintf(buf, "Speed variation limited to between %d.%d and %d.%d.\n",
  469.             u_speed_min(u) / 100, u_speed_min(u) % 100,
  470.             u_speed_max(u) / 100, u_speed_max(u) % 100);
  471.     }
  472.     if (u_speed_wind_effect(u) != lispnil) {
  473.         tprintf(buf, "Wind force affects speed.\n");
  474.     }
  475.     if (u_speed_wind_angle_effect(u) != lispnil) {
  476.         tprintf(buf, "Wind direction affects speed.\n");
  477.     }
  478.     if (u_speed_damage_effect(u) != lispnil) {
  479.         tprintf(buf, "Damage affects speed.\n");
  480.     }
  481.     ut_table_row_desc(spbuf, u, ut_mp_to_enter, NULL);
  482.     tprintf(buf, "MP to enter cell: %s.\n", spbuf);
  483.     ut_table_row_desc(spbuf, u, ut_mp_to_leave, NULL);
  484.     tprintf(buf, "MP to leave cell: %s.\n", spbuf);
  485.     if (any_mp_to_enter_unit(u)) {
  486.         uu_table_row_desc(spbuf, u, uu_mp_to_enter, NULL);
  487.         tprintf(buf, "MP to enter unit: %s.\n", spbuf);
  488.     }
  489.     if (any_mp_to_leave_unit(u)) {
  490.         uu_table_row_desc(spbuf, u, uu_mp_to_leave, NULL);
  491.         tprintf(buf, "MP to leave unit: %s.\n", spbuf);
  492.     }
  493.     if (u_mp_to_leave_world(u) >= 0) {
  494.         tprintf(buf, "%d MP to leave the world entirely.\n", u_mp_to_leave_world(u));
  495.     }
  496.     if (u_acp_to_move(u) > 0) {
  497.         tprintf(buf, "Uses %d ACP to move.\n", u_acp_to_move(u));
  498.     } else {
  499.         tprintf(buf, "Cannot move by self.\n");
  500.     }
  501.     } else {
  502.     tprintf(buf, "Does not move.\n");
  503.     }
  504.     tprintf(buf, "Hit Points: %d.", u_hp_max(u));
  505.     if (u_parts(u) > 1) {
  506.     tprintf(buf, "  Parts: %d.", u_parts(u));
  507.     }
  508.     if (u_hp_recovery(u) != 0) {
  509.     tprintf(buf, "  Recovers by %d.%d HP each turn.",
  510.         u_hp_recovery(u) / 100, u_hp_recovery(u) % 100);
  511.     }
  512.     tprintf(buf, "\n");
  513.     if (u_capacity(u) != 0) {
  514.     tprintf(buf, "Generic capacity for units is %d.\n",
  515.         u_capacity(u));
  516.     }
  517.     if (u_cxp_max(u) != 0) {
  518.     tprintf(buf, "Combat experience max: %d.\n", u_cxp_max(u));
  519.     }
  520.     if (u_cp(u) != 1) {
  521.     tprintf(buf, "Construction points to complete: %d.\n", u_cp(u));
  522.     }
  523.     if (u_tech_to_see(u) != 0) {
  524.     tprintf(buf, "Tech to see: %d.\n", u_tech_to_see(u));
  525.     }
  526.     if (u_tech_to_own(u) != 0) {
  527.     tprintf(buf, "Tech to own: %d.\n", u_tech_to_own(u));
  528.     }
  529.     if (u_tech_to_use(u) != 0) {
  530.     tprintf(buf, "Tech to use: %d.\n", u_tech_to_use(u));
  531.     }
  532.     if (u_tech_to_build(u) != 0) {
  533.     tprintf(buf, "Tech to build: %d.\n", u_tech_to_build(u));
  534.     }
  535.     if (u_tech_max(u) != 0) {
  536.     tprintf(buf, "Tech max: %d.\n", u_tech_max(u));
  537.     }
  538.     if (u_tech_max(u) != 0 && u_tech_per_turn_max(u) != 9999) {
  539.     tprintf(buf, "Tech increase per turn max: %d.\n", u_tech_per_turn_max(u));
  540.     }
  541.     if (u_tech_from_ownership(u) != 0) {
  542.     tprintf(buf, "Tech guaranteed by ownership: %d.\n", u_tech_from_ownership(u));
  543.     }
  544.     if (u_tech_leakage(u) != 0) {
  545.     tprintf(buf, "Tech leakage: %d.\n", u_tech_leakage(u));
  546.     }
  547.     if (u_acp(u) > 0
  548.         && type_can_research(u) > 0
  549.         ) {
  550.         tprintf(buf, "\nResearch:\n");
  551.         uu_table_row_desc(spbuf, u, uu_acp_to_research, NULL);
  552.     tprintf(buf, "ACP to research: %s.\n", spbuf);
  553.         uu_table_row_desc(spbuf, u, uu_tech_per_research, fraction_desc);
  554.     tprintf(buf, "  Tech gained: %s.\n", spbuf);
  555.     }
  556.     if (u_acp(u) > 0
  557.         && (type_can_create(u) > 0
  558.             || type_can_complete(u) > 0
  559.         )) {
  560.         tprintf(buf, "\nConstruction:\n");
  561.         if (type_can_create(u) > 0) {
  562.         uu_table_row_desc(spbuf, u, uu_acp_to_create, NULL);
  563.         tprintf(buf, "ACP to create: %s.\n", spbuf);
  564.          uu_table_row_desc(spbuf, u, uu_create_range, NULL);
  565.          tprintf(buf, "  Creation distance max: %s.\n", spbuf);
  566.          uu_table_row_desc(spbuf, u, uu_creation_cp, NULL);
  567.          tprintf(buf, "  Completeness upon creation: %s.\n", spbuf);
  568.     }
  569.         if (type_can_complete(u) > 0) {
  570.         uu_table_row_desc(spbuf, u, uu_acp_to_build, NULL);
  571.         tprintf(buf, "ACP to build: %s.\n", spbuf);
  572.          uu_table_row_desc(spbuf, u, uu_cp_per_build, NULL);
  573.          tprintf(buf, "  Completeness added per build: %s.\n", spbuf);
  574.         }
  575.         if (u_cp_per_self_build(u) > 0) {
  576.         tprintf(buf, "Can finish building self at %d cp, will add %d cp per action.\n",
  577.             u_cp_to_self_build(u), u_cp_per_self_build(u));
  578.         }
  579.         /* Toolup help. */
  580.         if (type_can_toolup(u)) {
  581.         uu_table_row_desc(spbuf, u, uu_acp_to_toolup, NULL);
  582.         tprintf(buf, "ACP to toolup: %s.\n", spbuf);
  583.         uu_table_row_desc(spbuf, u, uu_tp_per_toolup, NULL);
  584.         tprintf(buf, "  TP/toolup action: %s.\n", spbuf);
  585.         /* (should put these with type beING built...) */
  586.         uu_table_row_desc(spbuf, u, uu_tp_to_build, NULL);
  587.         tprintf(buf, "  TP to build: %s.\n", spbuf);
  588.         uu_table_row_desc(spbuf, u, uu_tp_max, NULL);
  589.         tprintf(buf, "  TP max: %s.\n", spbuf);
  590.         }
  591.         
  592.     }
  593.     if (u_acp(u) > 0
  594.         && (u_acp_to_fire(u) > 0
  595.         || type_can_attack(u) > 0
  596.         || u_acp_to_detonate(u) > 0
  597.         )) {
  598.     tprintf(buf, "\nCombat:\n");
  599.     if (type_can_attack(u) > 0) {
  600.         uu_table_row_desc(spbuf, u, uu_acp_to_attack, NULL);
  601.         tprintf(buf, "Can attack (ACP %s).\n", spbuf);
  602.         if (1 /* not always a range of 1 */) {
  603.         uu_table_row_desc(spbuf, u, uu_attack_range, NULL);
  604.         tprintf(buf, "Attack range is %s.\n", spbuf);
  605.         uu_table_row_desc(spbuf, u, uu_attack_range_min, NULL);
  606.         tprintf(buf, "Attack range min is %s.\n", spbuf);
  607.         }
  608.     }
  609.        if (u_acp_to_fire(u) > 0) {
  610.         tprintf(buf, "Can fire (%d ACP), at ranges", u_acp_to_fire(u));
  611.         if (u_range_min(u) > 0) {
  612.         tprintf(buf, " from %d", u_range_min(u));
  613.         }
  614.         tprintf(buf, " up to %d", u_range(u));
  615.         tprintf(buf, ".\n");
  616.     }
  617.     if (type_can_capture(u) > 0) {
  618.         uu_table_row_desc(spbuf, u, uu_capture, NULL);
  619.         tprintf(buf, "Can capture (ACP %s).\n", spbuf);
  620.         uu_table_row_desc(spbuf, u, uu_acp_to_capture, NULL);
  621.         tprintf(buf, "Chance to capture: %s.\n", spbuf);
  622.         /* (need indep capture also) */
  623.     }
  624.     if (u_acp_to_detonate(u) > 0) {
  625.         tprintf(buf, "Can detonate self (%d ACP)", u_acp_to_detonate(u));
  626.         if (u_hp_per_detonation(u) < u_hp_max(u)) {
  627.         tprintf(buf, ", losing %d HP per detonation",
  628.             u_hp_per_detonation(u));
  629.         }
  630.         tprintf(buf, ".\n");
  631.         if (u_detonate_on_death(u)) {
  632.         tprintf(buf, "%d%% chance to detonate if mortally hit in combat.\n",
  633.             u_detonate_on_death(u));
  634.         }
  635.         uu_table_row_desc(spbuf, u, uu_detonate_on_hit, NULL);
  636.         tprintf(buf, "Chance to detonate upon being hit: %s", spbuf);
  637.         tprintf(buf, ".\n");
  638.         uu_table_row_desc(spbuf, u, uu_detonate_on_capture, NULL);
  639.         tprintf(buf, "Chance to detonate upon capture: %s", spbuf);
  640.         tprintf(buf, ".\n");
  641.         uu_table_row_desc(spbuf, u, uu_detonation_range, NULL);
  642.         tprintf(buf, "Range of detonation effect is %s", spbuf);
  643.         tprintf(buf, ".\n");
  644.     }
  645.     uu_table_row_desc(spbuf, u, uu_hit, NULL);
  646.     tprintf(buf, "Hit chances are %s", spbuf);
  647.     tprintf(buf, ".\n");
  648.     uu_table_row_desc(spbuf, u, uu_damage, dice_desc);
  649.     tprintf(buf, "Damages are %s", spbuf);
  650.     tprintf(buf, ".\n");
  651.     }
  652.     if (u_wrecked_type(u) != NONUTYPE) {
  653.         tprintf(buf, "Becomes a %s when destroyed.\n",
  654.         u_type_name(u_wrecked_type(u)));
  655.     }
  656.     if (u_acp(u) > 0
  657.         && (u_acp_to_change_side(u) > 0
  658.             || u_acp_to_disband(u) > 0
  659.             || u_acp_to_transfer_part(u) > 0
  660.             )) {
  661.     tprintf(buf, "\nOther Actions:\n");
  662.     if (u_acp_to_change_side(u) > 0) {
  663.         tprintf(buf, "Can be given to another side (%d ACP).\n",
  664.             u_acp_to_change_side(u));
  665.     }
  666.     if (u_acp_to_disband(u) > 0) {
  667.         tprintf(buf, "Can be disbanded (%d ACP)", u_acp_to_disband(u));
  668.         if (u_hp_per_disband(u) < u_hp_max(u)) {
  669.             tprintf(buf, ", losing %d HP per action", u_hp_per_disband(u));
  670.         }
  671.         tprintf(buf, ".\n"); 
  672.     }
  673.     if (u_acp_to_transfer_part(u) > 0) {
  674.         tprintf(buf, "Can transfer parts (%d ACP).\n",
  675.             u_acp_to_transfer_part(u));
  676.     }
  677.     }
  678.     if (!g_see_all()) {
  679.         tprintf(buf, "\nVision:\n");
  680.     tprintf(buf, "%d%% chance to be seen at outset of game.\n",
  681.         u_already_seen(u));
  682.     tprintf(buf, "%d%% chance to be seen at outset of game if independent.\n",
  683.         u_already_seen_indep(u));
  684.     if (u_see_always(u))
  685.       tprintf(buf, "Always seen if terrain has been seen.\n");
  686.     /* (should put other _see_ things here) */
  687.     switch (u_vision_range(u)) {
  688.       case -1:
  689.         tprintf(buf, "Can never see other units.\n");
  690.         break;
  691.       case 0:
  692.         tprintf(buf, "Can see other units at own location.\n");
  693.         break;
  694.       case 1:
  695.         /* Default range, no need to say anything. */
  696.         break;
  697.       default:
  698.         tprintf(buf, "Can see units up to %d cells away.\n", u_vision_range(u));
  699.         break;
  700.     }
  701.     }
  702.     if (nummtypes > 0) {
  703.     tprintf(buf, "\nMaterial Handling:\n");
  704.     for_all_material_types(m) {
  705.         tprintf(buf, "  %s", m_type_name(m));
  706.         if (um_base_production(u, m) > 0) {
  707.         tprintf(buf, ", %d base production", um_base_production(u, m));
  708.         }
  709.         if (um_storage_x(u, m) > 0) {
  710.         tprintf(buf, ", %d storage", um_storage_x(u, m));
  711.         if (um_initial(u, m) > 0) {
  712.             tprintf(buf, " (%d initially)", min(um_initial(u, m), um_storage_x(u, m)));
  713.         }
  714.         }
  715.         if (um_base_consumption(u, m) > 0) {
  716.         tprintf(buf, ", %d base consumption", um_base_consumption(u, m));
  717.         }
  718.         if (um_inlength(u, m) > 0) {
  719.         tprintf(buf, ", receive from %d cells away", um_inlength(u, m));
  720.         }
  721.         if (um_outlength(u, m) > 0) {
  722.         tprintf(buf, ", send up to %d cells away", um_outlength(u, m));
  723.         }
  724.         tprintf(buf, "\n");
  725.     }
  726.     }
  727. #if 0
  728.     if (temperatures_defined() /* not correct, shouldn't look at state of world */) {
  729.     tprintf(buf, "Temperature survival zone is %d to %d, comfort zone is %d to %d.\n",
  730.         u_survival_min(u), u_survival_max(u), u_comfort_min(u), u_comfort_max(u));
  731.     }
  732. #endif
  733.     if (u_spy_chance(u) > 0 /* and random event in use */) {
  734.     tprintf(buf, "%d%% chance to spy, on units up to %d away.",
  735.         u_spy_chance(u), u_spy_range(u));
  736.     }
  737.     if (u_revolt(u) > 0 /* and random event in use */) {
  738.     tprintf(buf, "%d.%d%% chance of revolt.\n",
  739.         u_revolt(u) / 100, u_revolt(u) % 100);
  740.     }
  741.     /* Display the designer's notes for this type. */
  742.     if (u_notes(u) != lispnil) {
  743.     tprintf(buf, "\nNotes:\n");
  744.     append_notes(buf, u_notes(u));
  745.     }
  746. }
  747.  
  748. void
  749. append_help_phrase(buf, phrase)
  750. char *buf, *phrase;
  751. {
  752.     if (empty_string(phrase))
  753.       return;
  754.     tprintf(buf, "-- ");
  755.     tprintf(buf, "%s", phrase);
  756.     tprintf(buf, " --\n");
  757. }
  758.  
  759. void
  760. append_notes(buf, notes)
  761. char *buf;
  762. Obj *notes;
  763. {
  764.     char *notestr;
  765.     Obj *rest;
  766.  
  767.     if (stringp(notes)) {
  768.     notestr = c_string(notes);
  769.     if (strlen(notestr) > 0) { 
  770.         tprintf(buf, "%s", notestr);
  771.         tprintf(buf, " ");
  772.     } else {
  773.         tprintf(buf, "\n");
  774.     }
  775.     } else if (consp(notes)) {
  776.     for (rest = notes; rest != lispnil; rest = cdr(rest)) {
  777.         append_notes(buf, car(rest));
  778.     }
  779.     } else {
  780.     /* error? */
  781.     }
  782. }
  783.  
  784. void
  785. describe_mtype(m, key, buf)
  786. int m;
  787. char *key, *buf;
  788. {
  789.     append_help_phrase(buf, m_help(m));
  790.     if (m_people(m) > 0) {
  791.     tprintf(buf, "1 of this represents %d individuals.", m_people(m));
  792.     }
  793.     /* Display the designer's notes for this type. */
  794.     if (m_notes(m) != lispnil) {
  795.     tprintf(buf, "\nNotes:\n");
  796.     append_notes(buf, m_notes(m));
  797.     }
  798. }
  799.  
  800. void
  801. describe_ttype(t, key, buf)
  802. int t;
  803. char *key, *buf;
  804. {
  805.     int m, ct;
  806.  
  807.     append_help_phrase(buf, t_help(t));
  808.     switch (t_subtype(t)) {
  809.       case cellsubtype:
  810.     break;
  811.       case bordersubtype:
  812.     tprintf(buf, " (a border type)\n");
  813.     break;
  814.       case connectionsubtype:
  815.     tprintf(buf, " (a connection type)\n");
  816.     break;
  817.       case coatingsubtype:
  818.     tprintf(buf, " (a coating type)\n");
  819.     break;
  820.     }
  821.     tprintf(buf, "Generic unit capacity is %d.\n", t_capacity(t));
  822.     if (minelev != maxelev /* should be "elevscanvary" */) {
  823.     if (t_elev_min(t) == t_elev_max(t)) {
  824.         tprintf(buf, "Elevation is always %d.\n",
  825.             t_elev_min(t));
  826.     } else {
  827.         tprintf(buf, "Elevations fall between %d and %d.\n",
  828.             t_elev_min(t), t_elev_max(t));
  829.     }
  830.     }
  831.     if (any_temp_variation) {
  832.     if (t_temp_min(t) == t_temp_max(t)) {
  833.         tprintf(buf, "Temperature is always %d.\n",
  834.             t_temp_min(t));
  835.     } else {
  836.         tprintf(buf, "Temperatures fall between %d and %d, averaging %d.\n",
  837.             t_temp_min(t), t_temp_max(t), t_temp_avg(t));
  838.     }
  839.     }
  840.     if (any_wind_variation) {
  841.     if (t_wind_force_min(t) == t_wind_force_max(t)) {
  842.         tprintf(buf, "Wind force is always %d.\n",
  843.             t_wind_force_min(t));
  844.     } else {
  845.         tprintf(buf, "Wind forces fall between %d and %d, averaging %d.\n",
  846.             t_wind_force_min(t), t_wind_force_max(t), t_wind_force_avg(t));
  847.     }
  848.     /* (plus need wind variability) */
  849.     }
  850.     if (any_clouds) {
  851.     if (t_clouds_min(t) == t_clouds_max(t)) {
  852.         tprintf(buf, "Cloud cover is always %d.\n",
  853.             t_clouds_min(t));
  854.     } else {
  855.         tprintf(buf, "Cloud cover falls between %d and %d\n",
  856.             t_clouds_min(t), t_clouds_max(t));
  857.     }
  858.     }
  859.     /* Display relationships with materials. */
  860.     if (nummtypes > 0) {
  861.     for_all_material_types(m) {
  862.         if (tm_storage_x(t, m) > 0) {
  863.             tprintf(buf, "Can store up to %d %s", tm_storage_x(t, m), m_type_name(m));
  864.             tprintf(buf, " (normally starts game with %d)",
  865.                 min(tm_initial(t, m), tm_storage_x(t, m)));
  866.             tprintf(buf, ".\n");
  867.         }
  868.     }
  869.     }
  870.     /* Display relationships with any coating terrain types. */
  871.     if (numcoattypes > 0) {
  872.     tprintf(buf, "Coatings:\n");
  873.         for_all_terrain_types(ct) {
  874.         if (t_is_coating(ct)) {
  875.         tprintf(buf, "%s coats, depths %d up to %d",
  876.             t_type_name(ct), tt_coat_min(ct, t), tt_coat_max(ct, t));
  877.         }
  878.         }
  879.     }
  880.     /* Display the designer's notes for this type. */
  881.     if (t_notes(t) != lispnil) {
  882.     tprintf(buf, "\nNotes:\n");
  883.     append_notes(buf, t_notes(t));
  884.     }
  885. }
  886.  
  887. void
  888. describe_scorekeepers(arg, key, buf)
  889. int arg;
  890. char *key, *buf;
  891. {
  892.     int i = 1;
  893.     Scorekeeper *sk;
  894.  
  895.     if (scorekeepers == NULL) {
  896.     tprintf(buf, "No scores are being kept.");
  897.     } else {
  898.     for_all_scorekeepers(sk) {
  899.         if (numscorekeepers > 1) {
  900.         tprintf(buf, "%d.  ", i++);
  901.         }
  902.         if (symbolp(sk->body)
  903.         && match_keyword(sk->body, K_LAST_SIDE_WINS)) {
  904.         tprintf(buf, "The last side left in the game wins.");
  905.         /* (should mention point values also) */
  906.         } else {
  907.         tprintf(buf, "(an indescribably complicated scorekeeper)");
  908.         }
  909.         tprintf(buf, "\n");
  910.     }
  911.     }
  912. }
  913.  
  914. /* This describes a command (from cmd.def et al) in a way that all
  915.    interfaces can use. */
  916.  
  917. void
  918. describe_command (ch, name, help, onechar, buf)
  919. int ch, onechar;
  920. char *name, *help, *buf;
  921. {
  922.     if (onechar && ch != '\0') {
  923.     if (ch < ' ' || ch > '~') { 
  924.         sprintf(buf+strlen(buf), "'^%c' ", (ch ^ 0x40));
  925.     } else {
  926.         sprintf(buf+strlen(buf), " '%c' ", ch);
  927.     }
  928.     } else if (!onechar && ch == '\0') {
  929.     strcat(buf, "\"");
  930.     strcat(buf, name);
  931.     strcat(buf, "\"");
  932.     } else
  933.       return;
  934.     strcat(buf, " ");
  935.     strcat(buf, help);
  936.     strcat(buf, "\n");
  937. }
  938.  
  939. static int histogram_compare PROTO ((const void *h1, const void *h2));
  940.  
  941. struct histo {
  942.     int val, num;
  943. };
  944.  
  945. /* This compare will sort histogram entries in *reverse* order
  946.    (most common values first). */
  947.  
  948. static int
  949. histogram_compare(h1, h2)
  950. CONST void *h1, *h2;
  951. {
  952.     if (((struct histo *) h2)->num != ((struct histo *) h1)->num) {
  953.         return ((struct histo *) h2)->num - ((struct histo *) h1)->num;
  954.     } else {
  955.         return ((struct histo *) h2)->val - ((struct histo *) h1)->val;
  956.     }
  957. }
  958.  
  959. void
  960. uu_table_row_desc(buf, u, fn, formatter)
  961. char *buf;
  962. int u;
  963. int (*fn) PROTO ((int i, int j));
  964. void (*formatter) PROTO ((char *buf, int val));
  965. {
  966.     int val = (*fn)(u, 0), val2, u2, constant = TRUE, found;
  967.     int i, numentries, first;
  968.     struct histo histogram[MAXUTYPES];
  969.  
  970.     /* Compute a histogram of all the values in the row of the table. */
  971.     numentries = 0;
  972.     histogram[numentries].val = val;
  973.     histogram[numentries].num = 1;
  974.     ++numentries;
  975.     for_all_unit_types(u2) {
  976.     val2 = (*fn)(u, u2);
  977.     if (val2 == val) {
  978.         ++(histogram[0].num);
  979.     } else {
  980.         constant = FALSE;
  981.         found = FALSE;
  982.         for (i = 1; i < numentries; ++i) {
  983.         if (val2 == histogram[i].val) {
  984.             ++(histogram[i].num);
  985.             found = TRUE;
  986.             break;
  987.         }
  988.         }
  989.         if (!found) {
  990.         histogram[numentries].val = val2;
  991.         histogram[numentries].num = 1;
  992.         ++numentries;
  993.         }
  994.     }
  995.     }
  996.     /* The constant table/row case is easily disposed of. */
  997.     if (constant) {
  998.     if (formatter == NULL) {
  999.         sprintf(buf, "%d for all types", val);
  1000.         } else {
  1001.         (*formatter)(buf, val);
  1002.         strcat(buf, " for all types");
  1003.         }
  1004.         return;
  1005.     }
  1006.     /* Not a constant row; sort the histogram and compose a description. */
  1007.     qsort(histogram, numentries, sizeof(struct histo), histogram_compare);
  1008.     if (histogram[0].num * 2 >= numutypes) {
  1009.         if (formatter == NULL) {
  1010.         sprintf(buf, "%d by default", histogram[0].val);
  1011.         } else {
  1012.         char subbuf[40];
  1013.  
  1014.         (*formatter)(subbuf, histogram[0].val);
  1015.         sprintf(buf, "%s by default", subbuf);
  1016.         }
  1017.         i = 1;
  1018.     } else {
  1019.         sprintf(buf, "");
  1020.         i = 0;
  1021.     }
  1022.     for (; i < numentries; ++i) {
  1023.     if (i > 0) tnprintf(buf, BUFSIZE, ", ");
  1024.     if (formatter == NULL) {
  1025.         tnprintf(buf, BUFSIZE, "%d vs ", histogram[i].val);
  1026.     } else {
  1027.         char subbuf[40];
  1028.  
  1029.         (*formatter)(subbuf, histogram[i].val);
  1030.         tnprintf(buf, BUFSIZE, "%s vs ", subbuf);
  1031.     }
  1032.     first = TRUE;
  1033.     for_all_unit_types(u2) {
  1034.         if ((*fn)(u, u2) == histogram[i].val) {
  1035.         if (!first) tnprintf(buf, BUFSIZE, ",");  else first = FALSE;
  1036.         tnprintf(buf, BUFSIZE, "%s", u_type_name(u2));
  1037.         }
  1038.     }
  1039.     }
  1040. }
  1041.  
  1042. void
  1043. ut_table_row_desc(buf, u, fn, formatter)
  1044. char *buf;
  1045. int u;
  1046. int (*fn) PROTO ((int i, int j));
  1047. void (*formatter) PROTO ((char *buf, int val));
  1048. {
  1049.     int val = (*fn)(u, 0), val2, t, constant = TRUE, found;
  1050.     int i, numentries, first;
  1051.     struct histo histogram[MAXUTYPES];
  1052.  
  1053.     /* Compute a histogram of all the values in the row of the table. */
  1054.     numentries = 0;
  1055.     histogram[numentries].val = val;
  1056.     histogram[numentries].num = 1;
  1057.     ++numentries;
  1058.     for_all_terrain_types(t) {
  1059.     val2 = (*fn)(u, t);
  1060.     if (val2 == val) {
  1061.         ++(histogram[0].num);
  1062.     } else {
  1063.         constant = FALSE;
  1064.         found = FALSE;
  1065.         for (i = 1; i < numentries; ++i) {
  1066.         if (val2 == histogram[i].val) {
  1067.             ++(histogram[i].num);
  1068.             found = TRUE;
  1069.             break;
  1070.         }
  1071.         }
  1072.         if (!found) {
  1073.         histogram[numentries].val = val2;
  1074.         histogram[numentries].num = 1;
  1075.         ++numentries;
  1076.         }
  1077.     }
  1078.     }
  1079.     /* The constant table/row case is easily disposed of. */
  1080.     if (constant) {
  1081.     if (formatter == NULL) {
  1082.         sprintf(buf, "%d for all types", val);
  1083.         } else {
  1084.         (*formatter)(buf, val);
  1085.         strcat(buf, " for all types");
  1086.         }
  1087.         return;
  1088.     }
  1089.     /* Not a constant row; sort the histogram and compose a description. */
  1090.     qsort(histogram, numentries, sizeof(struct histo), histogram_compare);
  1091.     if (histogram[0].num * 2 >= numttypes) {
  1092.         if (formatter == NULL) {
  1093.         sprintf(buf, "%d by default", histogram[0].val);
  1094.         } else {
  1095.         char subbuf[40];
  1096.  
  1097.         (*formatter)(subbuf, histogram[0].val);
  1098.         sprintf(buf, "%s by default", subbuf);
  1099.         }
  1100.         i = 1;
  1101.     } else {
  1102.         sprintf(buf, "");
  1103.         i = 0;
  1104.     }
  1105.     for (; i < numentries; ++i) {
  1106.     if (i > 0)
  1107.       tnprintf(buf, BUFSIZE, ", ");
  1108.     if (formatter == NULL) {
  1109.         tnprintf(buf, BUFSIZE, "%d vs ", histogram[i].val);
  1110.     } else {
  1111.         char subbuf[40];
  1112.  
  1113.         (*formatter)(subbuf, histogram[i].val);
  1114.         tnprintf(buf, BUFSIZE, "%s vs ", subbuf);
  1115.     }
  1116.     first = TRUE;
  1117.     for_all_terrain_types(t) {
  1118.         if ((*fn)(u, t) == histogram[i].val) {
  1119.         if (!first)
  1120.           tnprintf(buf, BUFSIZE, ",");
  1121.         else
  1122.           first = FALSE;
  1123.         tnprintf(buf, BUFSIZE, "%s", t_type_name(t));
  1124.         }
  1125.     }
  1126.     }
  1127. }
  1128.  
  1129. void
  1130. um_table_row_desc(buf, u, fn, formatter)
  1131. char *buf;
  1132. int u;
  1133. int (*fn) PROTO ((int i, int j));
  1134. void (*formatter) PROTO ((char *buf, int val));
  1135. {
  1136.     int val = (*fn)(u, 0), val2, m, constant = TRUE, found;
  1137.     int i, numentries, first;
  1138.     struct histo histogram[MAXUTYPES];
  1139.  
  1140.     /* Compute a histogram of all the values in the row of the table. */
  1141.     numentries = 0;
  1142.     histogram[numentries].val = val;
  1143.     histogram[numentries].num = 1;
  1144.     ++numentries;
  1145.     for_all_material_types(m) {
  1146.     val2 = (*fn)(u, m);
  1147.     if (val2 == val) {
  1148.         ++(histogram[0].num);
  1149.     } else {
  1150.         constant = FALSE;
  1151.         found = FALSE;
  1152.         for (i = 1; i < numentries; ++i) {
  1153.         if (val2 == histogram[i].val) {
  1154.             ++(histogram[i].num);
  1155.             found = TRUE;
  1156.             break;
  1157.         }
  1158.         }
  1159.         if (!found) {
  1160.         histogram[numentries].val = val2;
  1161.         histogram[numentries].num = 1;
  1162.         ++numentries;
  1163.         }
  1164.     }
  1165.     }
  1166.     /* The constant table/row case is easily disposed of. */
  1167.     if (constant) {
  1168.     if (formatter == NULL) {
  1169.         sprintf(buf, "%d for all types", val);
  1170.         } else {
  1171.         (*formatter)(buf, val);
  1172.         strcat(buf, " for all types");
  1173.         }
  1174.         return;
  1175.     }
  1176.     /* Not a constant row; sort the histogram and compose a description. */
  1177.     qsort(histogram, numentries, sizeof(struct histo), histogram_compare);
  1178.     if (histogram[0].num * 2 >= nummtypes) {
  1179.         if (formatter == NULL) {
  1180.         sprintf(buf, "%d by default", histogram[0].val);
  1181.         } else {
  1182.         char subbuf[40];
  1183.  
  1184.         (*formatter)(subbuf, histogram[0].val);
  1185.         sprintf(buf, "%s by default", subbuf);
  1186.         }
  1187.         i = 1;
  1188.     } else {
  1189.         sprintf(buf, "");
  1190.         i = 0;
  1191.     }
  1192.     for (; i < numentries; ++i) {
  1193.     if (i > 0)
  1194.       tnprintf(buf, BUFSIZE, ", ");
  1195.     if (formatter == NULL) {
  1196.         tnprintf(buf, BUFSIZE, "%d vs ", histogram[i].val);
  1197.     } else {
  1198.         char subbuf[40];
  1199.  
  1200.         (*formatter)(subbuf, histogram[i].val);
  1201.         tnprintf(buf, BUFSIZE, "%s vs ", subbuf);
  1202.     }
  1203.     first = TRUE;
  1204.     for_all_material_types(m) {
  1205.         if ((*fn)(u, m) == histogram[i].val) {
  1206.         if (!first)
  1207.           tnprintf(buf, BUFSIZE, ",");
  1208.         else
  1209.           first = FALSE;
  1210.         tnprintf(buf, BUFSIZE, "%s", m_type_name(m));
  1211.         }
  1212.     }
  1213.     }
  1214. }
  1215.  
  1216. /* A simple table-printing utility. Blanks out default values so they don't
  1217.    clutter the table. */
  1218. /* (not currently used anywhere?) */
  1219.  
  1220. void
  1221. append_number(buf, value, dflt)
  1222. char *buf;
  1223. int value, dflt;
  1224. {
  1225.     if (value != dflt) {
  1226.     sprintf(tmpbuf, "%5d ", value);
  1227.     tprintf(buf, "%s", tmpbuf);
  1228.     } else {
  1229.     tprintf(buf, "      ");
  1230.     }
  1231. }
  1232.  
  1233. #ifdef USE_CONSOLE
  1234.  
  1235. /* Print the news file onto the console if there is anything to print. */
  1236.  
  1237. void
  1238. print_any_news()
  1239. {
  1240.     FILE *fp;
  1241.  
  1242.     fp = fopen(news_filename(), "r");
  1243.     if (fp != NULL) {
  1244.     printf("\n                              XCONQ NEWS\n\n");
  1245.     while (fgets(spbuf, BUFSIZE-1, fp) != NULL) {
  1246.         fputs(spbuf, stdout);
  1247.     }
  1248.     /* Add another blank line, to separate from init printouts. */
  1249.     printf("\n");
  1250.     fclose(fp);
  1251.     }
  1252. }
  1253.  
  1254. #endif /* USE_CONSOLE */
  1255.  
  1256. /* Generate a readable description of the game (design) being played. */
  1257. /* This works by writing out appropriate help nodes, along with some
  1258.    indexing material.  This does *not* do interface-specific help,
  1259.    such as commands. */
  1260.  
  1261. void
  1262. print_game_description_to_file(fp)
  1263. FILE *fp;
  1264. {
  1265.     HelpNode *node;
  1266.  
  1267.     /* (need to work on which nodes to dump out) */
  1268.     for (node = firsthelpnode; node != firsthelpnode; node = node->next) {
  1269.     get_help_text(node);
  1270.     if (node->text != NULL) {
  1271.         fprintf(fp, "\014\n%s\n", node->key);
  1272.         fprintf(fp, "%s\n", node->text);
  1273.     }
  1274.     }
  1275. }
  1276.